home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / system-config-printer / applet.py < prev    next >
Encoding:
Python Source  |  2009-05-05  |  15.0 KB  |  438 lines

  1. #!/usr/bin/env python
  2.  
  3. ## Copyright (C) 2007, 2008, 2009 Tim Waugh <twaugh@redhat.com>
  4. ## Copyright (C) 2007, 2008, 2009 Red Hat, Inc.
  5.  
  6. ## This program is free software; you can redistribute it and/or modify
  7. ## it under the terms of the GNU General Public License as published by
  8. ## the Free Software Foundation; either version 2 of the License, or
  9. ## (at your option) any later version.
  10.  
  11. ## This program is distributed in the hope that it will be useful,
  12. ## but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. ## GNU General Public License for more details.
  15.  
  16. ## You should have received a copy of the GNU General Public License
  17. ## along with this program; if not, write to the Free Software
  18. ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19.  
  20. import cups
  21. cups.require ("1.9.42")
  22. import sys
  23. import statereason
  24. from statereason import StateReason
  25. from debug import *
  26. import pprint
  27.  
  28. import dbus
  29. import dbus.glib
  30. import dbus.service
  31. import gobject
  32. import pynotify
  33. import time
  34. import locale
  35. import gettext
  36. from gettext import gettext as _
  37. DOMAIN="system-config-printer"
  38. gettext.textdomain (DOMAIN)
  39. statereason.set_gettext_function (_)
  40. try:
  41.     locale.setlocale (locale.LC_ALL, "")
  42. except locale.Error, e:
  43.     import os
  44.     os.environ['LC_ALL'] = 'C'
  45.     locale.setlocale (locale.LC_ALL, "")
  46.  
  47. APPDIR="/usr/share/system-config-printer"
  48. DOMAIN="system-config-printer"
  49. GLADE="applet.glade"
  50. ICON="printer"
  51. SEARCHING_ICON="document-print-preview"
  52.  
  53. # We need to call pynotify.init before we can check the server for caps
  54. pynotify.init('System Config Printer Notification')
  55.  
  56. ####
  57. #### NewPrinterNotification DBus server (the 'new' way).
  58. ####
  59. PDS_PATH="/com/redhat/NewPrinterNotification"
  60. PDS_IFACE="com.redhat.NewPrinterNotification"
  61. PDS_OBJ="com.redhat.NewPrinterNotification"
  62. class NewPrinterNotification(dbus.service.Object):
  63.     STATUS_SUCCESS = 0
  64.     STATUS_MODEL_MISMATCH = 1
  65.     STATUS_GENERIC_DRIVER = 2
  66.     STATUS_NO_DRIVER = 3
  67.  
  68.     def __init__ (self, bus):
  69.         self.bus = bus
  70.         self.getting_ready = 0
  71.         bus_name = dbus.service.BusName (PDS_OBJ, bus=bus)
  72.         dbus.service.Object.__init__ (self, bus_name, PDS_PATH)
  73.  
  74.     def wake_up (self):
  75.         global waitloop, runloop, viewer
  76.         import jobviewer
  77.         if viewer == None:
  78.             try:
  79.                 waitloop.quit ()
  80.             except:
  81.                 pass
  82.             runloop = gobject.MainLoop ()
  83.             viewer = jobviewer.JobViewer(bus=bus, loop=runloop,
  84.                                          service_running=service_running,
  85.                                          trayicon=trayicon,
  86.                                          suppress_icon_hide=True)
  87.  
  88.     @dbus.service.method(PDS_IFACE, in_signature='', out_signature='')
  89.     def GetReady (self):
  90.         self.wake_up ()
  91.         if self.getting_ready == 0:
  92.             viewer.set_special_statusicon (SEARCHING_ICON)
  93.  
  94.         self.getting_ready += 1
  95.         gobject.timeout_add (60 * 1000, self.timeout_ready)
  96.  
  97.     def timeout_ready (self):
  98.         global viewer
  99.         if self.getting_ready > 0:
  100.             self.getting_ready -= 1
  101.         if self.getting_ready == 0:
  102.             viewer.unset_special_statusicon ()
  103.  
  104.         return False
  105.  
  106.     @dbus.service.method(PDS_IFACE, in_signature='isssss', out_signature='')
  107.     def NewPrinter (self, status, name, mfg, mdl, des, cmd):
  108.         global viewer
  109.         self.wake_up ()
  110.  
  111.         if name.find("/") >= 0:
  112.             # name is a URI, no queue was generated, because no suitable
  113.             # driver was found
  114.             title = _("Missing printer driver")
  115.             devid = "MFG:%s;MDL:%s;DES:%s;CMD:%s;" % \
  116.                 (mfg, mdl, des, cmd)
  117.             if (mfg and mdl) or des:
  118.                 if (mfg and mdl):
  119.                     device = "%s %s" % (mfg, mdl)
  120.                 else:
  121.                     device = des
  122.                 text = _("No printer driver for %s.") % device
  123.             else:
  124.                 text = _("No driver for this printer.")
  125.             n = pynotify.Notification (title, text, 'printer')
  126.             if "actions" in pynotify.get_server_caps():
  127.                 n.set_urgency (pynotify.URGENCY_CRITICAL)
  128.                 n.add_action ("setup-printer", _("Search"),
  129.                               lambda x, y:
  130.                                   self.setup_printer (x, y, name, devid))
  131.             else:
  132.                 args = ["--setup-printer", name]
  133.                 if devid != "": args = args + ["--devid", devid]
  134.                 self.run_config_tool (args)
  135.  
  136.         else:
  137.             # name is the name of the queue which hal_lpadmin has set up
  138.             # automatically.
  139.             c = cups.Connection ()
  140.             try:
  141.                 printer = c.getPrinters ()[name]
  142.             except KeyError:
  143.                 return
  144.  
  145.             try:
  146.                 filename = c.getPPD (name)
  147.             except cups.IPPError:
  148.                 return
  149.  
  150.             del c
  151.  
  152.             # Check for missing packages
  153.             ppd = cups.PPD (filename)
  154.             import os
  155.             os.unlink (filename)
  156.             import sys
  157.             sys.path.append (APPDIR)
  158.             import cupshelpers
  159.             (missing_pkgs,
  160.              missing_exes) = cupshelpers.missingPackagesAndExecutables (ppd)
  161.  
  162.             from cupshelpers.ppds import ppdMakeModelSplit
  163.             (make, model) = ppdMakeModelSplit (printer['printer-make-and-model'])
  164.             driver = make + " " + model
  165.             if status < self.STATUS_GENERIC_DRIVER:
  166.                 title = _("Printer added")
  167.             else:
  168.                 title = _("Missing printer driver")
  169.  
  170.             if len (missing_pkgs) > 0:
  171.                 pkgs = reduce (lambda x,y: x + ", " + y, missing_pkgs)
  172.                 title = _("Install printer driver")
  173.                 text = _("`%s' requires driver installation: %s.") % (name, pkgs)
  174.                 n = pynotify.Notification (title, text)
  175.                 import installpackage
  176.                 if "actions" in pynotify.get_server_caps():
  177.                     try:
  178.                         self.packagekit = installpackage.PackageKit ()
  179.                         n.add_action ("install-driver", _("Install"),
  180.                                       lambda x, y:
  181.                                           self.install_driver (x, y,
  182.                                                                missing_pkgs))
  183.                     except:
  184.                         pass
  185.                 else:
  186.                     try:
  187.                         self.packagekit = installpackage.PackageKit ()
  188.                         self.packagekit.InstallPackageName (0, 0,
  189.                                                             missing_pkgs[0])
  190.                     except:
  191.                         pass
  192.  
  193.             elif status == self.STATUS_SUCCESS:
  194.                 text = _("`%s' is ready for printing.") % name
  195.                 n = pynotify.Notification (title, text)
  196.                 if "actions" in pynotify.get_server_caps():
  197.                     n.set_urgency (pynotify.URGENCY_NORMAL)
  198.                     n.add_action ("test-page", _("Print test page"),
  199.                                   lambda x, y:
  200.                                       self.print_test_page (x, y, name, devid))
  201.                     n.add_action ("configure", _("Configure"),
  202.                                   lambda x, y: self.configure (x, y, name))
  203.                 else:
  204.                     self.run_config_tool (["--configure-printer",
  205.                                            name, "--no-focus-on-map"])
  206.             else: # Model mismatch
  207.                 devid = "MFG:%s;MDL:%s;DES:%s;CMD:%s;" % \
  208.                     (mfg, mdl, des, cmd)
  209.                 text = _("`%s' has been added, using the `%s' driver.") % \
  210.                     (name, driver)
  211.                 n = pynotify.Notification (title, text, 'printer')
  212.                 if "actions" in pynotify.get_server_caps():
  213.                     n.set_urgency (pynotify.URGENCY_CRITICAL)
  214.                     n.add_action ("test-page", _("Print test page"),
  215.                                   lambda x, y:
  216.                                       self.print_test_page (x, y, name, devid))
  217.                     n.add_action ("find-driver", _("Find driver"),
  218.                                   lambda x, y: 
  219.                                   self.find_driver (x, y, name, devid))
  220.                     n.set_timeout (pynotify.EXPIRES_NEVER)
  221.                 else:
  222.                     self.run_config_tool (["--configure-printer",
  223.                                            name, "--no-focus-on-map"])
  224.  
  225.         viewer.notify_new_printer (name, n)
  226.         # Set the icon back how it was.
  227.         self.timeout_ready ()
  228.  
  229.     def run_config_tool (self, argv):
  230.         import os
  231.         pid = os.fork ()
  232.         if pid == 0:
  233.             # Child.
  234.             cmd = "/usr/bin/system-config-printer"
  235.             argv.insert (0, cmd)
  236.             os.execvp (cmd, argv)
  237.             sys.exit (1)
  238.         elif pid == -1:
  239.             print "Error forking process"
  240.         else:
  241.             gobject.timeout_add (60 * 1000, self.collect_exit_code, pid)
  242.  
  243.     def print_test_page (self, notification, action, name, devid = ""):
  244.         args = ["--print-test-page", name]
  245.         if devid != "":
  246.             args.extend (["--devid", devid])
  247.         self.run_config_tool (args)
  248.  
  249.     def configure (self, notification, action, name):
  250.         self.run_config_tool (["--configure-printer", name])
  251.  
  252.     def find_driver (self, notification, action, name, devid = ""):
  253.         args = ["--choose-driver", name]
  254.         if devid != "": args = args + ["--devid", devid]
  255.         self.run_config_tool (args)
  256.  
  257.     def setup_printer (self, notification, action, uri, devid = ""):
  258.         args = ["--setup-printer", uri]
  259.         if devid != "": args = args + ["--devid", devid]
  260.         self.run_config_tool (args)
  261.  
  262.     def install_driver (self, notification, action, missing_pkgs):
  263.         try:
  264.             self.packagekit.InstallPackageName (0, 0, missing_pkgs[0])
  265.         except:
  266.             pass
  267.  
  268.     def collect_exit_code (self, pid):
  269.         # We do this with timers instead of signals because we already
  270.         # have gobject imported, but don't (yet) import signal;
  271.         # let's try not to inflate the process size.
  272.         import os
  273.         try:
  274.             print "Waiting for child %d" % pid
  275.             (pid, status) = os.waitpid (pid, os.WNOHANG)
  276.             if pid == 0:
  277.                 # Run this timer again.
  278.                 return True
  279.         except OSError:
  280.             pass
  281.  
  282.         return False
  283.  
  284. PROGRAM_NAME="system-config-printer-applet"
  285. def show_help ():
  286.     print "usage: %s [--no-tray-icon]" % PROGRAM_NAME
  287.  
  288. def show_version ():
  289.     import config
  290.     print "%s %s" % (PROGRAM_NAME, config.VERSION)
  291.     
  292. ####
  293. #### Main program entry
  294. ####
  295.  
  296. global waitloop, runloop, viewer
  297.  
  298. trayicon = True
  299. service_running = False
  300. waitloop = runloop = None
  301. viewer = None
  302.  
  303. if __name__ == '__main__':
  304.     import sys, getopt
  305.     try:
  306.         opts, args = getopt.gnu_getopt (sys.argv[1:], '',
  307.                                         ['no-tray-icon',
  308.                                          'debug',
  309.                                          'help',
  310.                                          'version'])
  311.     except getopt.GetoptError:
  312.         show_help ()
  313.         sys.exit (1)
  314.  
  315.     for opt, optarg in opts:
  316.         if opt == "--help":
  317.             show_help ()
  318.             sys.exit (0)
  319.         if opt == "--version":
  320.             show_version ()
  321.             sys.exit (0)
  322.         if opt == "--no-tray-icon":
  323.             trayicon = False
  324.         elif opt == "--debug":
  325.             set_debugging (True)
  326.  
  327.     # Must be done before connecting to D-Bus (for some reason).
  328.     if not pynotify.init (PROGRAM_NAME):
  329.         try:
  330.             print >> sys.stderr, ("%s: unable to initialize pynotify" %
  331.                                   PROGRAM_NAME)
  332.         except:
  333.             pass
  334.  
  335.     if trayicon:
  336.         # Stop running when the session ends.
  337.         def monitor_session (*args):
  338.             pass
  339.  
  340.         try:
  341.             bus = dbus.SessionBus()
  342.             bus.add_signal_receiver (monitor_session)
  343.         except:
  344.             print >> sys.stderr, "%s: failed to connect to session D-Bus" % \
  345.                 PROGRAM_NAME
  346.             sys.exit (1)
  347.  
  348.     try:
  349.         bus = dbus.SystemBus()
  350.     except:
  351.         print >> sys.stderr, ("%s: failed to connect to system D-Bus" %
  352.                               PROGRAM_NAME)
  353.         sys.exit (1)
  354.  
  355.     if trayicon:
  356.         try:
  357.             NewPrinterNotification(bus)
  358.             service_running = True
  359.         except:
  360.             try:
  361.                 print >> sys.stderr, \
  362.                     "%s: failed to start NewPrinterNotification service" % \
  363.                     PROGRAM_NAME
  364.             except:
  365.                 pass
  366.  
  367.     if trayicon and get_debugging () == False:
  368.         # Start off just waiting for print jobs.
  369.         def any_jobs ():
  370.             try:
  371.                 c = cups.Connection ()
  372.                 jobs = c.getJobs (my_jobs=True, limit=1)
  373.                 if len (jobs):
  374.                     return True
  375.             except:
  376.                 pass
  377.  
  378.             return False
  379.  
  380.         if not any_jobs ():
  381.  
  382.             ###
  383.             class WaitForJobs:
  384.                 DBUS_PATH="/com/redhat/PrinterSpooler"
  385.                 DBUS_IFACE="com.redhat.PrinterSpooler"
  386.  
  387.                 def __init__ (self, bus, waitloop):
  388.                     self.bus = bus
  389.                     self.waitloop = waitloop
  390.                     self.timer = None
  391.                     bus.add_signal_receiver (self.handle_dbus_signal,
  392.                                              path=self.DBUS_PATH,
  393.                                              dbus_interface=self.DBUS_IFACE)
  394.  
  395.                 def __del__ (self):
  396.                     bus = self.bus
  397.                     bus.remove_signal_receiver (self.handle_dbus_signal,
  398.                                                 path=self.DBUS_PATH,
  399.                                                 dbus_interface=self.DBUS_IFACE)
  400.                     if self.timer:
  401.                         gobject.source_remove (self.timer)
  402.  
  403.                 def handle_dbus_signal (self, *args):
  404.                     if self.timer:
  405.                         gobject.source_remove (self.timer)
  406.                     self.timer = gobject.timeout_add (200, self.check_for_jobs)
  407.  
  408.                 def check_for_jobs (self, *args):
  409.                     debugprint ("checking for jobs")
  410.                     if any_jobs ():
  411.                         gobject.source_remove (self.timer)
  412.                         self.waitloop.quit ()
  413.  
  414.                     # Don't run this timer again.
  415.                     return False
  416.             ###
  417.  
  418.             waitloop = gobject.MainLoop ()
  419.             jobwaiter = WaitForJobs(bus, waitloop)
  420.             waitloop.run()
  421.             del jobwaiter
  422.             waitloop = None
  423.  
  424.     if viewer == None:
  425.         import jobviewer
  426.         import gtk
  427.         runloop = gobject.MainLoop ()
  428.         gtk.window_set_default_icon_name ('printer')
  429.         viewer = jobviewer.JobViewer(bus=bus, loop=runloop,
  430.                                      service_running=service_running,
  431.                                      trayicon=trayicon)
  432.  
  433.     try:
  434.         runloop.run()
  435.     except KeyboardInterrupt:
  436.         pass
  437.     viewer.cleanup ()
  438.